home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1996 / MacHack 1996.toast / Hacks / Hacks ’92 / IR Man™ / 2BufRecordToBufCmd.c copy next >
Encoding:
Text File  |  1992-06-06  |  16.9 KB  |  584 lines  |  [TEXT/MPS ]

  1. /*______________________________________________________*/
  2. /*                      Sound I/O Demo                    */
  3. /*                          by                          */
  4. /*                  RICHARD P. COLLYER                  */
  5. /*              Developer Technical Support             */
  6. /*                 Apple Computer, Inc.                 */
  7. /*                       11/14/91                       */
  8. /*______________________________________________________*/
  9.  
  10. /*
  11.  
  12.  
  13. */
  14.  
  15. /**********************************/
  16. /* #includes                      */
  17. /**********************************/
  18.  
  19. #include    "2BufRecordToBufCmd.h"
  20. #include     "atob.h"
  21. #include    "PlayMovie.h"
  22.  
  23. /**********************************/
  24. /* Application Globals              */
  25. /**********************************/
  26.  
  27. SPBPtr                gRecordStruct;
  28. SndChannelPtr        gChannel;
  29. Handle                gBufferHandle[kNumberOfBuffers];
  30. Fixed                gSampleRate;
  31. long                gInternalBuffer;
  32. long                gSoundRefNum = 0;
  33. unsigned long        gSampleAreaSize;        // size of the sample area in the snd handle
  34. int                    gHeaderSize;            // Size of the Header to be skipped in for the bufferCmd
  35. short                gDataStart;                // Size of the Entire 'snd ' header
  36. short                gSampleSize;
  37. short                gNumberOfChannels;
  38. short                gWhichRecordBuffer = 0;
  39. OSErr                gError;
  40. OSType                gCompression;
  41.  
  42. short                gWaitingToSend = 0;
  43. short                gSendingData = 0;
  44. short                gNetBufferSize = 0;
  45. short                gStandardBuffSize;
  46. Ptr                    gNetBuffers[kNumberOfBuffers];
  47. Ptr                    gCurrentNetBufferPoint;
  48. short                gCurrentNetBuffer;
  49.  
  50.  
  51. /**********************************/
  52. /* TestTheSystem                  */
  53. /**********************************/
  54.  
  55. void TestTheSystem (void)
  56.  
  57. /* use Gestalt to make sure the app will work on the system*/
  58.  
  59. {
  60.     long            feature;
  61.     
  62.     gError = Gestalt(gestaltSoundAttr, &feature);
  63.     if (!gError) {
  64.         /* First Check to see that Sound Input is available */
  65.         if ( !(feature & (1 << gestaltHasSoundInputDevice)) )
  66.             ExitWithMessage ("No Sound Input Device, so The app can't run", 0);
  67.             
  68.         /* Second Check to see that the hardware supports stereo, if not then I can't
  69.         record and play sounds at the same time */
  70.         if ( !(feature & (1 << gestaltStereoCapability)) )
  71.             ExitWithMessage ("No Stereo support, so The app can't run", 0);
  72.         }
  73.     else
  74.         ExitWithMessage ("Gestalt failed in TestTheSystem", gError);
  75.         
  76.     return;
  77. }
  78.  
  79. /**********************************/
  80. /* GetSoundDeviceInfo              */
  81. /**********************************/
  82.  
  83. void GetSoundDeviceInfo (void)
  84.  
  85. /* Extract the information about the Sound Input Device to build the Sound Header */
  86.  
  87. {
  88.     long        value;
  89.     
  90.     /* Get the sample rate information for the snd header */
  91.     
  92.     gSampleRate = 0x56EE8BA3;  /*  11 KHz = 0x2B7745D1 22 KHz = 0x56EE8BA3*/
  93.     
  94.     gError = SPBSetDeviceInfo (gSoundRefNum,siSampleRate, (Ptr) &gSampleRate);
  95.     if (gError != noErr)
  96.         ExitWithMessage ("SPBGetDeviceInfo failed in GetSoundDeviceInfo while looking at siSampleRate", gError);
  97.  
  98.     gError = SPBGetDeviceInfo (gSoundRefNum,siSampleRate, (Ptr) &gSampleRate);
  99.     if (gError != noErr)
  100.         ExitWithMessage ("SPBGetDeviceInfo failed in GetSoundDeviceInfo while looking at siSampleRate", gError);
  101.  
  102.     /* Get the sample size information for the snd header */
  103.         
  104.     gError = SPBGetDeviceInfo (gSoundRefNum,siSampleSize, (Ptr) &gSampleSize);
  105.     if (gError != noErr)
  106.         ExitWithMessage ("SPBGetDeviceInfo failed in GetSoundDeviceInfo while looking at siSampleSize", gError);
  107.         
  108.     /* Get the compression type information for the snd header */
  109.         
  110.     gError = SPBGetDeviceInfo (gSoundRefNum,siCompressionType, (Ptr) &gCompression);
  111.     if (gError != noErr)
  112.         ExitWithMessage ("SPBGetDeviceInfo failed in GetSoundDeviceInfo while looking at siCompressionType", gError);
  113.         
  114.     /* Get the number of input channels for the snd header */
  115.         
  116.     gError = SPBGetDeviceInfo (gSoundRefNum,siNumberChannels, (Ptr) &gNumberOfChannels);
  117.     if (gError != noErr)
  118.         ExitWithMessage ("SPBGetDeviceInfo failed in GetSoundDeviceInfo while looking at siNumberChannels", gError);
  119.         
  120.     /* Get the size of the internal sound buffer for the snd buffer */
  121.         
  122.     gError = SPBGetDeviceInfo (gSoundRefNum,siDeviceBufferInfo, (Ptr) &gInternalBuffer);
  123.     if (gError != noErr)
  124.         ExitWithMessage ("SPBGetDeviceInfo failed in GetSoundDeviceInfo while looking at siDeviceBufferInfo", gError);
  125.         
  126.     value = kMilliSecondsOfSound;
  127.     gError = SPBMillisecondsToBytes(gSoundRefNum, &value);
  128.     if (gError != noErr)
  129.         ExitWithMessage ((char *)"\pSPBMillisecondsToBytes failed in GetSoundDeviceInfo", gError);
  130.  
  131.     /* Round the buffer size to a multiple of the internal buffer size */
  132.     gSampleAreaSize = (value / gInternalBuffer) * gInternalBuffer;
  133.  
  134.     return;
  135. }
  136.  
  137. /**********************************/
  138. /* SetUpSounds                      */
  139. /**********************************/
  140.  
  141. void SetUpSounds (Handle *bufferHandle, short *headerSize)
  142.  
  143. /* SetUpSounds is a routine which allocates a snd buffer with the proper header
  144. and sample size, passing the handle and the real header size back to the caller. */
  145.  
  146. {
  147.     GetSoundDeviceInfo();
  148.     
  149.     /* Allocate largest Handle we could need */
  150.     *bufferHandle = NewHandle(gSampleAreaSize + kEstimatedHeaderSize);
  151.     gError = MemError();
  152.     if (gError != noErr || *bufferHandle == nil)
  153.         ExitWithMessage ("NewHandle failed in SetUpSounds", gError);
  154.     
  155.     /* Set up the header. After this call, we'll know how big the header size is */
  156.     gError = SetupSndHeader (*bufferHandle, gNumberOfChannels, gSampleRate, gSampleSize, gCompression, 
  157.                             kMiddleC, 0, headerSize);
  158.     if (gError != noErr)
  159.         ExitWithMessage ("SetupSndHeader failed in SetUpSounds", gError);
  160.     
  161.     /* Size the handle down to the size we really need */    
  162.     SetHandleSize(*bufferHandle, (Size) *headerSize + gSampleAreaSize);
  163.     gError = MemError();
  164.     if (gError != noErr)
  165.         ExitWithMessage ("SetHandleSize failed in SetUpSounds", gError);
  166.     
  167.     /* Move the handle high and lock it  */
  168.     MoveHHi (*bufferHandle);
  169.     gError = MemError();
  170.     if (gError != noErr)
  171.         ExitWithMessage ("MoveHHi failed in SetUpSounds", gError);
  172.  
  173.     HLock (*bufferHandle);
  174.     gError = MemError();
  175.     if (gError != noErr)
  176.         ExitWithMessage ("HLock failed in SetUpSounds", gError);
  177.  
  178.     return;
  179. }
  180.  
  181. /**********************************/
  182. /* FindHeaderSize                  */
  183. /**********************************/
  184.  
  185. int    FindHeaderSize (void)
  186.  
  187. /* This routine returns the number of bytes of the buffer we need to skip
  188. when calling bufferCmd.  The first several bytes of the sound header need to be skipped 
  189. so that the bufferCmd will be pointing at a SoundHeader Record and not an 'snd ' resource 
  190. header. The equations which are used in this routine are from Inside Macintosh VI page 20-22 */
  191.  
  192. {
  193.     int        headerSize;
  194.     short    highByte, lowByte;
  195.     short    format, numberOfSynths, numberOfCommands;
  196.     
  197.     highByte = **gBufferHandle[0];
  198.     lowByte = *(*gBufferHandle[0] + 1);
  199.     format = (highByte << 8) + lowByte;
  200.     
  201.     switch (format) {
  202.         case 1:        /* Format 1 snd */
  203.             headerSize = kBaseHeaderSize;
  204.             
  205.             // find the number of Synths in the snd header
  206.             highByte = *(*gBufferHandle[0] + 2);
  207.             lowByte = *(*gBufferHandle[0] + 3);
  208.             numberOfSynths = (highByte << 8) + lowByte;
  209.             headerSize += numberOfSynths * kSynthSize;
  210.             
  211.             // find the number of commands in the 'snd ' header
  212.             highByte = *(*gBufferHandle[0] + headerSize - 2);
  213.             lowByte = *(*gBufferHandle[0] + headerSize - 1);
  214.             numberOfCommands = (highByte << 8) + lowByte;
  215.             headerSize += numberOfCommands * kCmdSize;
  216.             break;
  217.  
  218.         case 2:        /* Format 2 snd */
  219.             headerSize = kBaseHeaderSize;
  220.             
  221.             // find the number of commands in the 'snd ' header
  222.             highByte = *(*gBufferHandle[0] + 4);
  223.             lowByte = *(*gBufferHandle[0] + 5);
  224.             numberOfCommands = (highByte << 8) + lowByte;
  225.             headerSize += numberOfCommands * kCmdSize;
  226.             break;
  227.  
  228.         default:
  229.             break;
  230.         }
  231.  
  232.     return (headerSize);
  233. }
  234.  
  235. /**********************************/
  236. /* BuildRecordStruct              */
  237. /**********************************/
  238.  
  239. void BuildRecordStruct (Handle bufferHandle)
  240.  
  241. /* build the gRecordStruct pointer and fill in the fields */
  242.  
  243. {
  244.     gRecordStruct = (SPBPtr) NewPtr(sizeof (SPB));
  245.     if (gRecordStruct == nil)
  246.         ExitWithMessage ("NewPtr failed in BuildRecordStruct", gError);
  247.  
  248.     gRecordStruct->inRefNum = gSoundRefNum;
  249.     gRecordStruct->count =  gSampleAreaSize;
  250.     gRecordStruct->milliseconds = 0;
  251.     gRecordStruct->bufferLength = gSampleAreaSize;
  252.     gRecordStruct->bufferPtr = (Ptr) ((*bufferHandle) + gDataStart);
  253.     gRecordStruct->completionRoutine = (ProcPtr) MyRecComp;
  254.     gRecordStruct->interruptRoutine = nil;
  255.     gRecordStruct->userLong = SetCurrentA5();
  256.     gRecordStruct->error = 0;
  257.     gRecordStruct->unused1 = 0;
  258.     
  259.     return;
  260. }
  261.  
  262. /**********************************/
  263. /* PlayBuffer                      */
  264. /**********************************/
  265.  
  266. void PlayBuffer (Handle bufferHandle)
  267.  
  268. /*This routine takes an 'snd ' buffer and a sound channel and turns the information into a 
  269. bufferCmd to the channel. */
  270.  
  271. {
  272.     SndCommand        localSndCmd;
  273.     
  274.     localSndCmd.cmd = bufferCmd;
  275.     localSndCmd.param1 = 0;
  276.     localSndCmd.param2 = (long) ((*bufferHandle) + gHeaderSize);
  277.     
  278.     gError = SndDoCommand (gChannel, &localSndCmd, false);
  279.     if (gError != noErr)
  280.         DebugStr("\pSndDoCommand failed in PlayBuffer (type 'g' return)");
  281.         
  282.     return;
  283. }
  284.  
  285. /**********************************/
  286. /* ExitWithMessage                  */
  287. /**********************************/
  288.  
  289. void ExitWithMessage (char *message, OSErr error)
  290.  
  291. /* All errors are passed to this routine to display a message and the error result.
  292. it also exits the application, because this sample demos the sound manager, not 
  293. error handleing.  */
  294.  
  295. {
  296.     GrafPtr                    savePort;
  297.     DialogPtr                myDialog;
  298.     short                    itemtype, itemHit;
  299.     Handle                    itemHand;
  300.     Rect                    itemRect;
  301.     char                    *errStrPtr, errStr[256];
  302.  
  303.     errStrPtr = (char *)&errStr;
  304.  
  305.     GetPort(&savePort);
  306.     myDialog = GetNewDialog(kErrorDialogID, nil, (WindowPtr) -1);
  307.     SetPort(myDialog);
  308.     
  309.     NumToString(error,errStrPtr);
  310.     GetDItem(myDialog,kErrNumStatText,&itemtype,&itemHand,&itemRect);
  311.     SetIText(itemHand, errStrPtr);
  312.  
  313.     GetDItem(myDialog,kMsgStatText,&itemtype,&itemHand,&itemRect);
  314.     SetIText(itemHand, message);
  315.  
  316.     do {
  317.         ModalDialog(nil,&itemHit);
  318.         } while (itemHit != kOKButton);
  319.             
  320.     DisposDialog(myDialog);
  321.     SetPort(savePort);
  322.     
  323.     TimeToQuit ();
  324. }
  325.  
  326.  
  327. /**********************************/
  328. /* TimeToQuit                      */
  329. /**********************************/
  330.  
  331. void TimeToQuit (void)
  332.  
  333. /* Once I am out of the loop it is time to clean up - stop the currently playing sound,
  334. Dispose of the Channel, close the input driver, and dispose of the Buffer handles and
  335. gRecordStruct Ptr. */
  336.  
  337. {
  338.     short                index, recordingStat, meterlevel;
  339.     unsigned long        totalSamples, numberOfSamples, totalMSec, numberOfMSec;
  340.     
  341.     // check each global to make sure they were allocated before disposing of them
  342.     
  343.     if (gSoundRefNum != 0) {
  344.         gError = SPBGetRecordingStatus (gSoundRefNum, &recordingStat, &meterlevel,
  345.                                 &totalSamples, &numberOfSamples, &totalMSec, &numberOfMSec);
  346.         if (gError != noErr)
  347.             DebugStr("\SPBGetRecordingStatus failed in TimeToQuit (type 'g' return)");
  348.             
  349.         if (recordingStat > 0) {
  350.             // make sure that recording has stopped before I close the sound driver
  351.             gError = SPBStopRecording (gSoundRefNum);
  352.             if (gError != noErr)
  353.                 DebugStr("\pSPBStopRecording failed in TimeToQuit (type 'g' return)");
  354.             }
  355.             
  356.         gError = SPBCloseDevice (gSoundRefNum);
  357.         if (gError != noErr)
  358.             DebugStr("\pSPBCloseDevice failed in TimeToQuit (type 'g' return)");
  359.         }
  360.         
  361.     if (gBufferHandle[0] != nil)
  362.         for (index = 0; index < kNumberOfBuffers; ++index)
  363.             DisposeHandle (gBufferHandle[index]);
  364.  
  365.     if (gRecordStruct != nil)
  366.         DisposePtr ((Ptr) gRecordStruct);
  367.  
  368.     if (gChannel != nil) {
  369.         gError = SndDisposeChannel (gChannel,true);
  370.         if (gError != noErr)
  371.             DebugStr("\pSndDisposeChannel failed in TimeToQuit (type 'g' return)");
  372.         }
  373.     
  374. }
  375.  
  376. /**********************************/
  377. /* main                              */
  378. /**********************************/
  379.  
  380. void
  381. InitSoundStuff()
  382. {
  383.     short                index, contOnOff = 1;
  384.     short                dataSize;
  385.     
  386.  
  387.     /* Open sound input drive (whichever one is selected in the sound cdev) */
  388.     
  389.     gError = SPBOpenDevice (kDefaultDriver, siWritePermission, &gSoundRefNum);
  390.     if (gError != noErr)
  391.         ExitWithMessage ("SPBOpenDevice failed in main", gError);
  392.         
  393.     /* turn on continuous recording */
  394.         
  395.     gError = SPBSetDeviceInfo (gSoundRefNum,siContinuous, (Ptr) &contOnOff);
  396.     if (gError != noErr)
  397.         ExitWithMessage ("SPBSetDeviceInfo failed in main", gError);
  398.         
  399.     /* build the kNumberOfBuffers snd Buffers */
  400.         
  401.     for (index = 0; index < kNumberOfBuffers; ++index)
  402.         SetUpSounds (&gBufferHandle[index], &gDataStart);
  403.     
  404.     /* determine the part of the header which needs to be skipped before calling bufferCmd */
  405.     
  406.     gHeaderSize = FindHeaderSize();
  407.     
  408.     if (gHeaderSize != 20)
  409.         Debugger();
  410.         
  411.     /* build the gRecordStruct pointer and fill in the fields */
  412.         
  413.     BuildRecordStruct (gBufferHandle[gWhichRecordBuffer]);
  414.  
  415.     /* Build Network Data Buffers */
  416.     gStandardBuffSize = GetHandleSize(gBufferHandle[gWhichRecordBuffer]);
  417.     
  418.     for (index = 0; index < kNumberOfBuffers; ++index)
  419.         gNetBuffers[index] = NewPtr(kNetBufferSize);
  420.         
  421.     gCurrentNetBufferPoint = gNetBuffers[0];
  422.     gCurrentNetBuffer = 0;
  423.     
  424.     /* open the sound channel which I will need to play from */
  425.  
  426.     gChannel = nil;
  427.  
  428.     // initNoInterp gets rid of the clicks between the buffers
  429.     gError = SndNewChannel (&gChannel, sampledSynth, initNoInterp, nil);
  430.     if (gError != noErr)
  431.         ExitWithMessage ("SndNewChannel failed in main", gError);
  432. }
  433.  
  434. void
  435. StartRecord() {
  436.     gError = SPBRecord (gRecordStruct, true); // start recording
  437.     if (gError != noErr)
  438.         ExitWithMessage ("SPBRecord failed in main", gError);
  439. }
  440.  
  441. long
  442. SendSoundData() {
  443.     short        dataSize;
  444.     short        extraData;
  445.     long        returnedData = 0;
  446.     Ptr                sndDataPtr;
  447.     long            sndDataLen;
  448.     EventRecord     myEvent;
  449.     
  450.     gWaitingToSend = 1;
  451.     
  452.     while ((gWaitingToSend == 1) && (!Button())) {
  453.         WaitNextEvent(0, &myEvent, 0, nil);
  454.         playMovies(&myEvent);
  455.  
  456.     }
  457.     
  458.     
  459.     if (!Button()) {
  460.         gSendingData = 1;
  461.                 
  462.         dataSize = gNetBufferSize;
  463.         extraData = gCurrentNetBuffer;
  464.         extraData = NextBuffer (extraData);
  465.         sndDataLen = *((long *) gNetBuffers[extraData]);
  466.         sndDataPtr = gNetBuffers[extraData] + sizeof(long);
  467.         returnedData = AnalizeData((unsigned char *)(sndDataPtr), sndDataLen);
  468.             
  469.         gSendingData = 0;
  470.     }
  471.     else
  472.     {
  473.         extraData = 1;
  474.         dataSize = sizeof(short);
  475. /*        SendData(&dataSize, (Ptr)&extraData); */
  476.     }
  477.     return(returnedData);
  478. }
  479.  
  480. void
  481. SoundCleanUpAndQuit() {
  482.     short                dataSize;
  483.  
  484.     gNetBufferSize = 0;
  485.     dataSize = sizeof(short);
  486. /*    SendData(&dataSize, (Ptr)&gNetBufferSize); */
  487.  
  488.     // quitting time
  489.         
  490.     if (gRecordStruct->error < noErr)
  491.         ExitWithMessage ((char *)"\pAn error occurred while recording", gRecordStruct->error);
  492.     else
  493.         TimeToQuit ();
  494. }
  495.  
  496. /**********************************/
  497. /* MyRecComp                      */
  498. /**********************************/
  499.  
  500. pascal void MyRecComp (SPBPtr inParamPtr)
  501.  
  502. // This is the Completion Routine which is called every time the recording Buffer,
  503. // is full.  This routine needs to setup A5 to be the application's A5 which was being
  504. // saved in the userLong of the parameter block.
  505.  
  506. // Due to the MPW C compiler optimization scheme, access to global arrays will be pointed
  507. // to in an address register as an offset of A5.  This will happen before we have a chance
  508. // to set A5 to our application's A5.  To avoid this we need to first restore our A5 and
  509. // then call the completion routine.
  510.  
  511. {
  512.     long                storeA5;
  513.     
  514.     /* Set A5 so the completion routine has access to the Application Globals */
  515.  
  516.  
  517.      storeA5 = SetA5 (inParamPtr->userLong);
  518.  
  519.     RealCompletion ();
  520.  
  521.     storeA5 = SetA5 (storeA5);
  522.  
  523.     return;
  524. }
  525.  
  526. /**********************************/
  527. /* RealCompletion                  */
  528. /**********************************/
  529.  
  530. void RealCompletion (void)
  531.  
  532. // Setup the current snd handle to have the correct size of the sample data we've just
  533. // finished recording.  The size of the sample data is in the count field of the
  534. // recording parameter block.  Then play this new buffer of data.  Finally, we will
  535. // start another recording after switching to the other buffer.
  536.  
  537. {
  538.     OSErr            err;
  539.     SoundHeaderPtr    header;
  540.  
  541.     // if there has been an error then do nothing
  542.     if (gRecordStruct->error < 0) return;
  543.     
  544.     // get pointer to SoundHeader and update length value in the header
  545.     header = (SoundHeaderPtr) (*(gBufferHandle[gWhichRecordBuffer]) + gHeaderSize);
  546.     header->length = gRecordStruct->count;
  547.     
  548. /*    gNetBuffer = *gBufferHandle[gWhichRecordBuffer]; */
  549.     
  550.     /* If there is enough room, blockmove more in  */
  551.     if (((gCurrentNetBufferPoint - gNetBuffers[gCurrentNetBuffer]) + gStandardBuffSize + 1024) <
  552.             kNetBufferSize) { 
  553.         BlockMove ((Ptr)&header->length,
  554.                         gCurrentNetBufferPoint, sizeof(long));
  555.         gCurrentNetBufferPoint += sizeof(long);
  556.  
  557.         BlockMove (&(header->sampleArea[0]), gCurrentNetBufferPoint, header->length);
  558.         gCurrentNetBufferPoint += gStandardBuffSize;
  559.         
  560.     }
  561.     
  562.     if (gWaitingToSend == 1) {
  563.         gNetBufferSize = (gCurrentNetBufferPoint - gNetBuffers[gCurrentNetBuffer]);
  564.  
  565.         gCurrentNetBuffer = NextBuffer (gCurrentNetBuffer); // move on to next buffer
  566.         gCurrentNetBufferPoint = gNetBuffers[gCurrentNetBuffer];
  567.         gWaitingToSend = 0;
  568.     }
  569.     
  570. /*    PlayBuffer (gBufferHandle[gWhichRecordBuffer]);    */    // Play the buffer
  571.     
  572.     gWhichRecordBuffer = NextBuffer (gWhichRecordBuffer);    // move on to next buffer
  573.     
  574.      // update gRecordStruct
  575.      gRecordStruct->bufferPtr = (*(gBufferHandle[gWhichRecordBuffer]) + gDataStart);
  576.     gRecordStruct->milliseconds = 0;
  577.     gRecordStruct->count = gSampleAreaSize;
  578.     gRecordStruct->bufferLength = gSampleAreaSize;
  579.     
  580.     err = SPBRecord (gRecordStruct, true);        // queue up another record
  581.     if (err) 
  582.         DebugStr ("\pSPBRecord died in RealCompletion");
  583. }
  584.